"""
Command-line interface for GCR Solver Manager.
"""

import asyncio
import sys
from pathlib import Path
from typing import Optional

import click
from rich.console import Console
from rich.table import Table
from rich.progress import Progress, SpinnerColumn, TextColumn

from ..core.config import get_config, reload_config
from ..core.exceptions import GSMError
from ..database.session import (
    get_database_manager, 
    init_database, 
    close_database
)

console = Console()


def handle_exceptions(func):
    """Decorator to handle exceptions and display them nicely."""
    def wrapper(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except GSMError as e:
            console.print(f"[red]Error: {e}[/red]")
            if e.details:
                console.print(f"[yellow]Details: {e.details}[/yellow]")
            sys.exit(1)
        except Exception as e:
            console.print(f"[red]Unexpected error: {e}[/red]")
            if get_config().debug:
                console.print_exception()
            sys.exit(1)
    return wrapper


def async_command(func):
    """Decorator to run async commands."""
    def wrapper(*args, **kwargs):
        return asyncio.run(func(*args, **kwargs))
    return wrapper


@click.group()
@click.option('--config', '-c', type=click.Path(exists=True), help='Configuration file path')
@click.option('--verbose', '-v', is_flag=True, help='Enable verbose output')
@click.option('--debug', is_flag=True, help='Enable debug mode')
@click.pass_context
def main(ctx, config, verbose, debug):
    """GCR Solver Manager - Build and Log Management System."""
    # Ensure context object exists
    ctx.ensure_object(dict)
    
    # Load configuration
    if config:
        from ..core.config import Config
        ctx.obj['config'] = Config.load_from_file(config)
    else:
        ctx.obj['config'] = get_config()
    
    # Set verbosity and debug
    ctx.obj['verbose'] = verbose or ctx.obj['config'].debug
    ctx.obj['debug'] = debug or ctx.obj['config'].debug
    
    if ctx.obj['verbose']:
        console.print(f"[blue]GCR Solver Manager v{get_version()}[/blue]")
        console.print(f"[dim]Config: {ctx.obj['config'].environment}[/dim]")


def get_version() -> str:
    """Get package version."""
    try:
        from .. import __version__
        return __version__
    except ImportError:
        return "unknown"


# Database Commands
@main.group()
def db():
    """Database management commands."""
    pass


@db.command()
@click.option('--drop', is_flag=True, help='Drop existing tables first')
@click.pass_context
@handle_exceptions
@async_command
async def init(ctx, drop):
    """Initialize the database."""
    with Progress(
        SpinnerColumn(),
        TextColumn("[progress.description]{task.description}"),
        console=console,
    ) as progress:
        task = progress.add_task("Initializing database...", total=None)
        
        try:
            await init_database(drop_existing=drop)
            progress.update(task, description="Database initialized successfully!")
            console.print("[green]✓ Database initialization complete[/green]")
        except Exception as e:
            progress.update(task, description=f"Failed: {e}")
            raise


@db.command()
@click.pass_context
@handle_exceptions
def info(ctx):
    """Show database information."""
    manager = get_database_manager()
    info = manager.get_database_info()
    
    table = Table(title="Database Information")
    table.add_column("Property", style="cyan")
    table.add_column("Value", style="green")
    
    table.add_row("URL", info['url'])
    table.add_row("Type", info['engine_type'])
    table.add_row("Pool Size", str(info['pool_size']))
    table.add_row("Echo", str(info['echo']))
    table.add_row("Connection", "✓ OK" if manager.check_connection() else "✗ Failed")
    
    console.print(table)


@db.command()
@click.confirmation_option(prompt='Are you sure you want to drop all tables?')
@click.pass_context
@handle_exceptions
@async_command
async def reset(ctx):
    """Reset the database (drop and recreate tables)."""
    with Progress(
        SpinnerColumn(),
        TextColumn("[progress.description]{task.description}"),
        console=console,
    ) as progress:
        task = progress.add_task("Resetting database...", total=None)
        
        try:
            await init_database(drop_existing=True)
            progress.update(task, description="Database reset successfully!")
            console.print("[green]✓ Database reset complete[/green]")
        except Exception as e:
            progress.update(task, description=f"Failed: {e}")
            raise


# Configuration Commands
@main.group()
def config():
    """Configuration management commands."""
    pass


@config.command()
@click.pass_context
@handle_exceptions
def show(ctx):
    """Show current configuration."""
    cfg = ctx.obj['config']
    
    table = Table(title="Configuration")
    table.add_column("Section", style="cyan")
    table.add_column("Key", style="blue")
    table.add_column("Value", style="green")
    
    # Flatten configuration for display
    config_dict = cfg.to_dict()
    for section, values in config_dict.items():
        if isinstance(values, dict):
            for key, value in values.items():
                table.add_row(section, key, str(value))
        else:
            table.add_row("", section, str(values))
    
    console.print(table)


@config.command()
@click.option('--output', '-o', type=click.Path(), help='Output file path')
@click.pass_context
@handle_exceptions
def export(ctx, output):
    """Export configuration to file."""
    cfg = ctx.obj['config']
    
    if not output:
        output = f"config_export_{cfg.environment}.yaml"
    
    output_path = Path(output)
    cfg.save_to_file(output_path)
    
    console.print(f"[green]✓ Configuration exported to {output_path}[/green]")


@config.command()
@click.pass_context
@handle_exceptions
def reload(ctx):
    """Reload configuration from files."""
    try:
        new_config = reload_config()
        ctx.obj['config'] = new_config
        console.print("[green]✓ Configuration reloaded[/green]")
    except Exception as e:
        raise GSMError(f"Failed to reload configuration: {e}")


# Build Commands
@main.group()
def build():
    """Build management commands."""
    pass


@build.command()
@click.option('--gpu', type=click.Choice(['cuda', 'hip']), help='GPU type')
@click.option('--solver', type=click.Choice(['gcr', 'ca-gcr', 'gmres', 'bca-gmres']), help='Solver type')
@click.option('--debug', is_flag=True, help='Enable debug build')
@click.option('--clean', is_flag=True, help='Clean build')
@click.option('--parallel', '-j', type=int, help='Number of parallel jobs')
@click.option('--config-name', help='Build configuration name')
@click.pass_context
@handle_exceptions
def run(ctx, gpu, solver, debug, clean, parallel, config_name):
    """Run a build."""
    console.print("[yellow]Build functionality not yet implemented[/yellow]")
    console.print("Parameters received:")
    console.print(f"  GPU: {gpu}")
    console.print(f"  Solver: {solver}")
    console.print(f"  Debug: {debug}")
    console.print(f"  Clean: {clean}")
    console.print(f"  Parallel jobs: {parallel}")
    console.print(f"  Config name: {config_name}")


@build.command()
@click.pass_context
@handle_exceptions
def list(ctx):
    """List build configurations."""
    console.print("[yellow]Build configuration listing not yet implemented[/yellow]")


@build.command()
@click.option('--older-than', type=int, help='Clean builds older than N days')
@click.pass_context
@handle_exceptions
def clean(ctx, older_than):
    """Clean build artifacts."""
    console.print("[yellow]Build cleaning not yet implemented[/yellow]")


# Test Commands
@main.group()
def test():
    """Test management commands."""
    pass


@test.command()
@click.option('--solver', type=click.Choice(['gcr', 'ca-gcr', 'gmres', 'bca-gmres']), help='Solver type')
@click.option('--nproc', type=int, help='Number of MPI processes')
@click.option('--resolution', type=float, help='Resolution parameter')
@click.option('--debug', is_flag=True, help='Run in debug mode')
@click.option('--slurm', is_flag=True, help='Use SLURM for execution')
@click.option('--remote', help='Remote host for execution')
@click.pass_context
@handle_exceptions
def run(ctx, solver, nproc, resolution, debug, slurm, remote):
    """Run a test."""
    console.print("[yellow]Test functionality not yet implemented[/yellow]")
    console.print("Parameters received:")
    console.print(f"  Solver: {solver}")
    console.print(f"  Processes: {nproc}")
    console.print(f"  Resolution: {resolution}")
    console.print(f"  Debug: {debug}")
    console.print(f"  SLURM: {slurm}")
    console.print(f"  Remote: {remote}")


@test.command()
@click.option('--status', type=click.Choice(['pending', 'running', 'completed', 'failed', 'cancelled']))
@click.option('--limit', type=int, default=10, help='Number of results to show')
@click.pass_context
@handle_exceptions
def list(ctx, status, limit):
    """List test runs."""
    console.print("[yellow]Test listing not yet implemented[/yellow]")


@test.command()
@click.argument('test_id', type=int)
@click.pass_context
@handle_exceptions
def show(ctx, test_id):
    """Show test run details."""
    console.print(f"[yellow]Test details for ID {test_id} not yet implemented[/yellow]")


# Log Commands
@main.group()
def logs():
    """Log management commands."""
    pass


@logs.command()
@click.option('--test-run', type=int, help='Test run ID')
@click.option('--type', 'log_type', type=click.Choice(['build', 'test', 'debug', 'slurm', 'error']))
@click.option('--level', type=click.Choice(['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL']))
@click.option('--limit', type=int, default=10, help='Number of results to show')
@click.option('--follow', '-f', is_flag=True, help='Follow log output')
@click.pass_context
@handle_exceptions
def list(ctx, test_run, log_type, level, limit, follow):
    """List log entries."""
    console.print("[yellow]Log listing not yet implemented[/yellow]")


@logs.command()
@click.option('--latest', is_flag=True, help='Compare latest logs')
@click.option('--files', multiple=True, help='Specific log files to compare')
@click.pass_context
@handle_exceptions
def compare(ctx, latest, files):
    """Compare logs and analyze norms."""
    console.print("[yellow]Log comparison not yet implemented[/yellow]")


@logs.command()
@click.option('--query', help='Query string')
@click.option('--start-date', help='Start date (YYYY-MM-DD)')
@click.option('--end-date', help='End date (YYYY-MM-DD)')
@click.option('--limit', type=int, default=100, help='Number of results to show')
@click.pass_context
@handle_exceptions
def search(ctx, query, start_date, end_date, limit):
    """Search logs."""
    console.print("[yellow]Log search not yet implemented[/yellow]")


@logs.command()
@click.option('--older-than', type=int, help='Clean logs older than N days')
@click.option('--compress', is_flag=True, help='Compress old logs instead of deleting')
@click.pass_context
@handle_exceptions
def clean(ctx, older_than, compress):
    """Clean old logs."""
    console.print("[yellow]Log cleaning not yet implemented[/yellow]")


# Analysis Commands
@main.group()
def analysis():
    """Analysis and reporting commands."""
    pass


@analysis.command()
@click.option('--solver', help='Solver type to analyze')
@click.option('--days', type=int, default=7, help='Number of days to analyze')
@click.option('--output', '-o', help='Output file path')
@click.pass_context
@handle_exceptions
def performance(ctx, solver, days, output):
    """Generate performance analysis report."""
    console.print("[yellow]Performance analysis not yet implemented[/yellow]")


@analysis.command()
@click.option('--test-run-1', type=int, required=True, help='First test run ID')
@click.option('--test-run-2', type=int, required=True, help='Second test run ID')
@click.option('--output', '-o', help='Output file path')
@click.pass_context
@handle_exceptions
def compare(ctx, test_run_1, test_run_2, output):
    """Compare two test runs."""
    console.print("[yellow]Test run comparison not yet implemented[/yellow]")


@analysis.command()
@click.option('--solver', help='Solver type')
@click.option('--metric', help='Metric to analyze')
@click.option('--days', type=int, default=30, help='Number of days to analyze')
@click.pass_context
@handle_exceptions
def trends(ctx, solver, metric, days):
    """Analyze performance trends."""
    console.print("[yellow]Trend analysis not yet implemented[/yellow]")


# API Commands
@main.group()
def api():
    """API server commands."""
    pass


@api.command()
@click.option('--host', help='Host to bind to')
@click.option('--port', type=int, help='Port to bind to')
@click.option('--reload', is_flag=True, help='Enable auto-reload')
@click.pass_context
@handle_exceptions
def start(ctx, host, port, reload):
    """Start the API server."""
    console.print("[yellow]API server not yet implemented[/yellow]")


# Status Command
@main.command()
@click.pass_context
@handle_exceptions
def status(ctx):
    """Show system status."""
    config = ctx.obj['config']
    
    table = Table(title="System Status")
    table.add_column("Component", style="cyan")
    table.add_column("Status", style="green")
    table.add_column("Details", style="dim")
    
    # Database status
    try:
        manager = get_database_manager()
        db_status = "✓ Connected" if manager.check_connection() else "✗ Disconnected"
        db_details = manager.get_database_info()['url']
    except Exception as e:
        db_status = "✗ Error"
        db_details = str(e)
    
    table.add_row("Database", db_status, db_details)
    
    # Configuration status
    config_status = f"✓ Loaded ({config.environment})"
    config_details = f"Debug: {config.debug}"
    table.add_row("Configuration", config_status, config_details)
    
    # Build system status
    build_status = "⚠ Not checked"
    build_details = f"Source: {config.build.source_dir}"
    table.add_row("Build System", build_status, build_details)
    
    console.print(table)


# Version Command
@main.command()
def version():
    """Show version information."""
    console.print(f"GCR Solver Manager v{get_version()}")


if __name__ == '__main__':
    main()